package cn.baiweigang.uu.service.linearswap;

import cn.baiweigang.uu.enums.DirectionEnum;
import cn.baiweigang.uu.enums.OffsetEnum;
import cn.baiweigang.uu.enums.StrategyStatusEnum;
import cn.baiweigang.uu.enums.TradeStatusEnum;
import cn.baiweigang.uu.model.AccountInfo;
import cn.baiweigang.uu.model.KLineInfo;
import cn.baiweigang.uu.model.StrategyInfo;
import cn.baiweigang.uu.model.TradeInfo;
import cn.baiweigang.uu.service.DbService;
import cn.baiweigang.uu.utils.DateUtil;
import cn.baiweigang.uu.utils.LogUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.Date;

@Service
public class StrategyService {
    @Autowired
    private DbService dbService;
    @Autowired
    private UpOrDownService upOrDownService;
    @Autowired
    private OrderService orderService;
    @Autowired
    private KLineService kLineService;

    /**
     * 策略初始化
     *
     * @return
     */
    public StrategyInfo init(AccountInfo accountInfo) {
        StrategyInfo strategyInfo = new StrategyInfo();
        strategyInfo.setName(this.getClass().getSimpleName());
        strategyInfo.setStrategyHistory(new ArrayList<>());
        strategyInfo.setDateTimeTs(DateUtil.getTs());
        strategyInfo.setCurrentStrategyStatus(StrategyStatusEnum.INIT);
        strategyInfo.setMemo("新的一轮开始");
        strategyInfo.getStrategyHistory().add(
                new StrategyInfo(strategyInfo.getCurrentStrategyStatus(),
                        DateUtil.formatDateYMDHMS(new Date(strategyInfo.getDateTimeTs()))));
        //读取最新订单汇总信息
        strategyInfo.setSummarySheetInfo(
                dbService.queryDay1(accountInfo, DateUtil.formatDateYMD()));
        strategyInfo.setAccountInfo(accountInfo);
        strategyInfo.setKLineList(kLineService.getHistoryKLineMin5(accountInfo.getContractCode(), 6));
        return strategyInfo;
    }

    /**
     * 推进到下一阶段
     *
     * @param strategyInfo
     * @param strategyStatus
     * @param memo
     */
    public void advance(StrategyInfo strategyInfo, StrategyStatusEnum strategyStatus, String memo) {
        if (strategyInfo == null ||
                strategyStatus == StrategyStatusEnum.INIT) {
            LogUtil.logError("状态非法 不允许推进到INIT状态");
            return;
        }
        strategyInfo.setPreStrategyStatus(strategyInfo.getCurrentStrategyStatus());
        strategyInfo.setDateTimeTs(DateUtil.getTs());
        strategyInfo.setCurrentStrategyStatus(strategyStatus);
        strategyInfo.setMemo(memo);
        strategyInfo.getStrategyHistory().add(
                new StrategyInfo(strategyInfo.getCurrentStrategyStatus(),
                        DateUtil.formatDateYMDHMS(new Date(strategyInfo.getDateTimeTs()))));
    }

    public void orderAllDeal(StrategyInfo strategyInfo, TradeInfo tradeInfo) {
        //订单成交
        LogUtil.logInfo("收到订单成交信息");
        //更新订单成交时间
        strategyInfo.setOrderDealTs(DateUtil.getTs());
        if (tradeInfo.getOffset() == OffsetEnum.OPEN) {
            LogUtil.logInfo("开仓订单成交了");
            //把止盈止损记录下
            tradeInfo.setTpOrderPrice(strategyInfo.getOpenOrder().getTpOrderPrice());
            tradeInfo.setTpOrderPriceType(strategyInfo.getOpenOrder().getTpOrderPriceType());
            tradeInfo.setTpTriggerPrice(strategyInfo.getOpenOrder().getTpTriggerPrice());

            tradeInfo.setSlOrderPrice(strategyInfo.getOpenOrder().getSlOrderPrice());
            tradeInfo.setSlOrderPriceType(strategyInfo.getOpenOrder().getSlOrderPriceType());
            tradeInfo.setSlTriggerPrice(strategyInfo.getOpenOrder().getSlTriggerPrice());

            strategyInfo.setOpenOrder(tradeInfo);
            strategyInfo.setOrderDealTs(DateUtil.getTs());
            advance(strategyInfo, StrategyStatusEnum.OPEN_ORDER_DEAL, "开仓成功");
        } else if (tradeInfo.getOffset() == OffsetEnum.CLOSE) {
            LogUtil.logInfo("平仓订单成交了");
            if (strategyInfo.getOpenOrder() == null || strategyInfo.getOpenOrder().getStatus() != TradeStatusEnum.ALL_DEAL) {
                LogUtil.logError("当前还未开仓或未成交，收到平仓订单信息，抛弃处理");
                LogUtil.logError(tradeInfo);
                return;
            }
            strategyInfo.setCloseOrder(tradeInfo);
            advance(strategyInfo, StrategyStatusEnum.CLOSE_ORDER_DEAL, "平仓成功");
            //平仓订单成交，允许下次开仓时间在配置的时间之后
            LogUtil.logInfo("设置下次开仓时间在 " + strategyInfo.getCloseAndOpenMinutes() + " 分钟之后");
            strategyInfo.setAllowNextOpenTs(DateUtil.getTs() + strategyInfo.getCloseAndOpenMinutes() * 60 * 1000);
        } else {
            LogUtil.logError("订单方向非法");
        }

    }

    public void orderSubmited(StrategyInfo strategyInfo, TradeInfo tradeInfo) {
        //委托单提交成功
        LogUtil.logInfo("收到委托订单提交成功");
        if (tradeInfo.getOffset() == OffsetEnum.OPEN) {
            LogUtil.logInfo("开仓委托订单提交成功");
            strategyInfo.setOpenOrder(tradeInfo);
            advance(strategyInfo, StrategyStatusEnum.EXIST_OPEN_ORDER, "开仓委托单提交成功");
        } else if (tradeInfo.getOffset() == OffsetEnum.CLOSE) {
            LogUtil.logInfo("平委托订单提交成功");
            strategyInfo.setCloseOrder(tradeInfo);
            advance(strategyInfo, StrategyStatusEnum.EXIST_CLOSE_ORDER, "平仓委托单提交成功");
        } else {
            LogUtil.logError("订单方向非法");
        }
    }

    public void submitOpenOrder(StrategyInfo strategyInfo, KLineInfo kLineInfo) {
        if (strategyInfo.getOpenOrder() == null) {
            if (!isAllowOpenOrder(strategyInfo.getAllowNextOpenTs())) {
                LogUtil.logInfo("当前不允许开仓，可开仓时间= " + DateUtil.formatDateYMDHMS(new Date(strategyInfo.getAllowNextOpenTs())));
                return;
            }
            int up = strategyInfo.getUpScore();
            int down = strategyInfo.getDownScore();
            int openScore = strategyInfo.getOpenScore();
            LogUtil.logInfo("上涨趋势得分=" + up + " 下降趋势得分=" + down);

            //上涨趋势分数大于配置值 且 当前价格大于前一K线收盘价
            KLineInfo pre = strategyInfo.getKLineList().get(4);
            if (up >= openScore && kLineInfo.getClose().doubleValue() > pre.getClose().doubleValue()) {
                //要向上涨 开仓多单
                LogUtil.logInfo("满足多单开仓条件");
                //提交委托单
                orderService.openOrder(strategyInfo, DirectionEnum.BUY, kLineInfo);
            }
            //下降趋势分数大于配置值 且 当前价格小于于前一K线收盘价
            else if (down >= openScore && kLineInfo.getClose().doubleValue() < pre.getClose().doubleValue()) {
                //要向下跌 开仓空单
                LogUtil.logInfo("满足空单开仓条件 ");
                //提交委托单
                orderService.openOrder(strategyInfo, DirectionEnum.SELL, kLineInfo);
            } else {
                LogUtil.logInfo("此次不满足开仓委托下单条件");
                LogUtil.logInfo("当前价=" + kLineInfo.getClose().doubleValue() + " 前一周期=" + pre.getDateTime() + " 收盘价=" + pre.getClose().doubleValue());
            }
        } else {
            LogUtil.logError("已经持有仓位，不需要再开仓");
        }
    }

    /**
     * 当前是否允许开仓
     *
     * @return
     */
    private boolean isAllowOpenOrder(long allowNextOpenTs) {
        return allowNextOpenTs < 0 || DateUtil.getTs() > allowNextOpenTs;
    }

    public void checkOpenOrder(StrategyInfo strategyInfo, KLineInfo kLineInfo) {
        if (strategyInfo.getOpenOrder() != null) {
            long holdOpenTs = DateUtil.getTs() - strategyInfo.getOpenOrder().getCreatedGmt();
            LogUtil.logInfo("开仓委托单等地成交中 已等待 " + holdOpenTs / 1000 + " 秒");
            if (strategyInfo.getOpenOrder().getStatus() == TradeStatusEnum.SUBMITED &&
                    holdOpenTs > strategyInfo.getWaitOpenMaxMinutes() * 60 * 1000) {
                LogUtil.logInfo("委托单超过最大等待时间还未成交，需要重新委托下单");
                orderService.cancelOpenOrders(strategyInfo.getAccountInfo());
                strategyInfo.setOpenOrder(null);
                advance(strategyInfo, StrategyStatusEnum.WAIT_TO_OPEN, "开仓委托单等待超时 需要重新下委托单");
            }
        } else {
            advance(strategyInfo, StrategyStatusEnum.WAIT_TO_OPEN, "不存在开仓委托单 需要重新下委托单");
        }
    }

    public void checkPosition(StrategyInfo strategyInfo, KLineInfo kLineInfo) {
        //先刷新下盈亏、触发止损盈亏
        updateTpsl(strategyInfo, kLineInfo);
        long holdTs = DateUtil.getTs() - strategyInfo.getOrderDealTs();
        double tpslVolume = strategyInfo.getTpslVolume();
        double slPriceCurrentVolume = strategyInfo.getSlPriceCurrentVolume();
        //持仓信息打印
        LogUtil.logInfo("持仓时间= " + holdTs / 1000 / 60 + " 分钟，盈亏点数= " +
                tpslVolume + " 开仓价格=" + strategyInfo.getOpenOrder().getTradeAvgPrice() +
                " 开仓方向=" + strategyInfo.getOpenOrder().getDirection().getCode() + " 止损设置=" +
                strategyInfo.getOpenOrder().getSlOrderPrice() + " 如果止损盈亏=" + slPriceCurrentVolume);
        //最大盈亏信息记录
        if (tpslVolume > 0 && tpslVolume > strategyInfo.getOpenOrder().getMaxTpVolume()) {
            strategyInfo.getOpenOrder().setMaxTpVolume(tpslVolume);
        } else if (tpslVolume < 0 && tpslVolume < strategyInfo.getOpenOrder().getMaxSlVolume()) {
            strategyInfo.getOpenOrder().setMaxSlVolume(tpslVolume);
        }

        int up = strategyInfo.getUpScore();
        int down = strategyInfo.getDownScore();
        int[][] tpN = strategyInfo.getTpN();
        int[][] slN = strategyInfo.getSlN();
        int[][] tpIfslN = strategyInfo.getTpIfslN();
        int[][] slDynamic = strategyInfo.getSlDynamic();

        KLineInfo pre = strategyInfo.getKLineList().get(4);
        LogUtil.logInfo("上涨趋势得分=" + up + " 下降趋势得分=" + down + " 前一周期" + pre.getDateTime() + " 收盘价=" +
                pre.getClose().doubleValue());
        //刷新盈亏点数、止损盈亏点数
        //达到盈利点位 强平
        for (int[] tpConfig : tpN) {
            if (checkTp(strategyInfo, tpConfig[0], tpConfig[1], tpConfig[2], kLineInfo, up, down)) {
                LogUtil.logInfo("命中盈利平仓配置，中止盈利检查");
                return;
            }
        }
        //达到亏损点位 强平
        for (int[] slConfig : slN) {
            if (checkSl(strategyInfo, slConfig[0], slConfig[1], slConfig[2], kLineInfo, up, down)) {
                LogUtil.logInfo("命中亏损平仓配置，中止亏损检查");
                return;
            }
        }
        //曾亏损过，达到盈利xx 立即平仓
        for (int[] tpConfig : tpIfslN) {
            if (checkTpIfSl(strategyInfo, tpConfig[0], tpConfig[1], tpConfig[2], kLineInfo)) {
                LogUtil.logInfo("命中曾亏损过，目前盈利配置，中止盈利检查");
                return;
            }
        }

        //动态更新止损 盈亏大于150 才更新止损配置
        if (tpslVolume > 150) {
            LogUtil.logInfo("开始动态止损检查");
            for (int[] slConfig : slDynamic) {
                if (checkSlDynamic(strategyInfo, slConfig[0], slConfig[1], slConfig[2], kLineInfo)) {
                    LogUtil.logInfo("命中动态更新止损策略，中止动态执行检查");
                    return;
                }
            }
        }
    }


    /**
     * 持仓大于xx分钟  盈利大于xx 开始平仓
     *
     * @param holdMinutes
     * @param tpVolume
     */
    private boolean checkTp(StrategyInfo strategyInfo, int holdMinutes,
                            int tpVolume, int score, KLineInfo kLineInfo, int up, int down) {
        //当前持仓时间
        long holdTs = DateUtil.getTs() - strategyInfo.getOrderDealTs();
        KLineInfo pre = strategyInfo.getKLineList().get(4);
        //持仓时间大于xx，达到第一盈利点位 强平
        if (holdTs > holdMinutes * 60 * 1000) {
            if (strategyInfo.getOpenOrder().getDirection() == DirectionEnum.BUY &&
                    kLineInfo.getClose().subtract(strategyInfo.getOpenOrder().getTradeAvgPrice()).doubleValue() >= tpVolume &&
                    (score < 0 ||
                            (down >= score &&
                                    kLineInfo.getClose().doubleValue() < pre.getClose().doubleValue()
                            )
                    )
            ) {
                String forceCloseMemo = "多单持仓已 " + holdTs / 1000 / 60 + " 分钟且当前盈利中，达到盈利目标 " + tpVolume + " ，且当前趋势下降分>=" + score + " 触发强平";
                LogUtil.logInfo(forceCloseMemo);
                strategyInfo.setForceCloseMemo(forceCloseMemo);
                orderService.forceCleanPosition(strategyInfo.getOpenOrder());
                return true;
            } else if (strategyInfo.getOpenOrder().getDirection() == DirectionEnum.SELL &&
                    strategyInfo.getOpenOrder().getTradeAvgPrice().subtract(kLineInfo.getClose()).doubleValue() >= tpVolume &&
                    (score < 0 ||
                            (up >= score &&
                                    kLineInfo.getClose().doubleValue() > pre.getClose().doubleValue()
                            )
                    )
            ) {
                String forceCloseMemo = "空单持仓已 " + holdTs / 1000 / 60 + " 分钟且当前盈利中，达到盈利目标 " + tpVolume + " ，且当前趋势上升分>=" + score + " 触发强平";
                LogUtil.logInfo(forceCloseMemo);
                strategyInfo.setForceCloseMemo(forceCloseMemo);
                orderService.forceCleanPosition(strategyInfo.getOpenOrder());
                return true;
            }
        }
        return false;
    }

    private boolean checkSl(StrategyInfo strategyInfo, int holdMinutes,
                            int slVolume, int score, KLineInfo kLineInfo, int up, int down) {
        //当前持仓时间
        long holdTs = DateUtil.getTs() - strategyInfo.getOrderDealTs();
        KLineInfo pre = strategyInfo.getKLineList().get(4);
        if (holdTs > holdMinutes * 60 * 1000) {
            //多单亏损 下降趋势，强平
            if (strategyInfo.getOpenOrder().getDirection() == DirectionEnum.BUY &&
                    strategyInfo.getOpenOrder().getTradeAvgPrice().subtract(kLineInfo.getClose()).intValue() >= slVolume &&
                    (score < 0 ||
                            (down >= score &&
                                    kLineInfo.getClose().doubleValue() < pre.getClose().doubleValue()
                            )
                    )
            ) {
                String forceCloseMemo = "多单持仓已 " + holdTs / 1000 / 60 + " 分钟且亏损超过" + slVolume + "，当前趋势下降分>=" + score + " 触发强平";
                LogUtil.logInfo(forceCloseMemo);
                strategyInfo.setForceCloseMemo(forceCloseMemo);
                orderService.forceCleanPosition(strategyInfo.getOpenOrder());
                return true;
            }
            //空单 上升趋势，强平
            else if (strategyInfo.getOpenOrder().getDirection() == DirectionEnum.SELL &&
                    kLineInfo.getClose().subtract(strategyInfo.getOpenOrder().getTradeAvgPrice()).intValue() >= slVolume &&
                    (score < 0 ||
                            (up >= score &&
                                    kLineInfo.getClose().doubleValue() > pre.getClose().doubleValue()
                            )
                    )
            ) {
                String forceCloseMemo = "空单持仓已 " + holdTs / 1000 / 60 + " 分钟且亏损超过" + slVolume + "，当前趋势上升分>=" + score + " 触发强平";
                LogUtil.logInfo(forceCloseMemo);
                strategyInfo.setForceCloseMemo(forceCloseMemo);
                orderService.forceCleanPosition(strategyInfo.getOpenOrder());
                return true;
            }
        }
        return false;
    }


    private boolean checkTpIfSl(StrategyInfo strategyInfo, int holdMinutes,
                                int slVolume, int tpVolume, KLineInfo kLineInfo) {
        //当前持仓时间
        long holdTs = DateUtil.getTs() - strategyInfo.getOrderDealTs();
        if (holdTs > holdMinutes * 60 * 1000 && strategyInfo.getOpenOrder().getMaxSlVolume() <= slVolume) {
            if (strategyInfo.getOpenOrder().getDirection() == DirectionEnum.BUY &&
                    kLineInfo.getClose().subtract(strategyInfo.getOpenOrder().getTradeAvgPrice()).doubleValue() >= tpVolume) {
                String forceCloseMemo = "多单持仓已 " + holdTs / 1000 / 60 + " 分钟，曾经最大亏损>=" + slVolume + " 目前达到盈利目标 " + tpVolume + " 触发强平";
                LogUtil.logInfo(forceCloseMemo);
                strategyInfo.setForceCloseMemo(forceCloseMemo);
                orderService.forceCleanPosition(strategyInfo.getOpenOrder());
                return true;
            } else if (strategyInfo.getOpenOrder().getDirection() == DirectionEnum.SELL &&
                    strategyInfo.getOpenOrder().getTradeAvgPrice().subtract(kLineInfo.getClose()).doubleValue() >= tpVolume) {
                String forceCloseMemo = "空单持仓已 " + holdTs / 1000 / 60 + " 分钟，曾经最大亏损>=" + slVolume + " 目前达到盈利目标 " + tpVolume + " 触发强平";
                LogUtil.logInfo(forceCloseMemo);
                strategyInfo.setForceCloseMemo(forceCloseMemo);
                orderService.forceCleanPosition(strategyInfo.getOpenOrder());
                return true;
            }
        }
        return false;
    }

    private boolean checkSlDynamic(StrategyInfo strategyInfo, int holdMinutes, int tpVolume, int slVolume, KLineInfo kLineInfo) {
        //当前持仓时间
        long holdTs = DateUtil.getTs() - strategyInfo.getOrderDealTs();
        double tpslVolume = strategyInfo.getTpslVolume();
        double slPriceCurrentVolume = strategyInfo.getSlPriceCurrentVolume();
//        LogUtil.logInfo("tpslVolume=" + tpslVolume + " slPriceCurrentVolume=" + slPriceCurrentVolume);
//        LogUtil.logInfo("tpVolume=" + tpVolume + " slVolume=" + slVolume);
        if (holdTs > holdMinutes * 60 * 1000 &&
                tpslVolume > tpVolume &&
                slVolume > slPriceCurrentVolume) {
            String memo = "盈利达到" + tpslVolume + " 更新动态止损价，触发止损时 盈利=" + slVolume;
            LogUtil.logInfo(memo);
            strategyInfo.setForceCloseMemo(memo);
            //先撤销止盈止损
            orderService.tpslOrderCancelAll(strategyInfo.getAccountInfo());
            return orderService.tpslOrderRequest(strategyInfo, slVolume);
        }
        return false;
    }

    public void checkCloseOrder(StrategyInfo strategyInfo, KLineInfo kLineInfo) {
        if (strategyInfo.getCloseOrder() != null &&
                strategyInfo.getCloseOrder().getStatus() == TradeStatusEnum.SUBMITED &&
                DateUtil.getTs() - strategyInfo.getCloseOrder().getCreatedGmt() > strategyInfo.getWaitOpenMaxMinutes() * 60 * 1000) {
            String memo = "委托单超过最大等待时间还未成交，需要重新委托下单";
            LogUtil.logInfo(memo);
            orderService.cancelOpenOrders(strategyInfo.getAccountInfo());
            strategyInfo.setCloseOrder(null);
            advance(strategyInfo, StrategyStatusEnum.WAIT_TO_CLOSE, memo);
        } else if (strategyInfo.getCloseOrder() == null) {
            String memo = "当前没有平仓委托单 状态异常，推进到WAIT_TO_CLOSE 等待提交平仓委托单";
            advance(strategyInfo, StrategyStatusEnum.WAIT_TO_CLOSE, memo);
        }
    }

    public void submitCloseOrder(StrategyInfo strategyInfo, KLineInfo kLineInfo) {
        orderService.forceCleanPosition(strategyInfo.getOpenOrder());
    }

    public void endRound(StrategyInfo strategyInfo) {
        LogUtil.logInfo("已关单，此轮结束");
        advance(strategyInfo, StrategyStatusEnum.END, "已关单，此轮结束");
    }

    /**
     * 更新盈亏点数、止损设置和当前价盈亏点数
     *
     * @param strategyInfo
     * @param kLineInfo
     */
    public void updateTpsl(StrategyInfo strategyInfo, KLineInfo kLineInfo) {
        try {
            double tpslVolume = 0;
            double slPriceCurrentVolume = 0;
            TradeInfo openOrder = strategyInfo.getOpenOrder();
            if (openOrder.getDirection() == DirectionEnum.BUY) {
                tpslVolume = kLineInfo.getClose().subtract(
                        strategyInfo.getOpenOrder().getTradeAvgPrice()).doubleValue();
                slPriceCurrentVolume = openOrder.getSlOrderPrice().subtract(
                        openOrder.getPrice()).doubleValue();
            } else {
                tpslVolume = strategyInfo.getOpenOrder().getTradeAvgPrice().subtract(
                        kLineInfo.getClose()).doubleValue();
                slPriceCurrentVolume = openOrder.getPrice().subtract(
                        openOrder.getSlOrderPrice()).doubleValue();
            }
            strategyInfo.setTpslVolume(tpslVolume);
            strategyInfo.setSlPriceCurrentVolume(slPriceCurrentVolume);
        } catch (Throwable e) {
            LogUtil.logError("更新盈亏点数出错 " + e.getMessage());
        }

    }
}
